package com.hzncc.flowable_diboot.util;


import com.diboot.core.util.S;
import com.hzncc.flowable_diboot.contract.entity.ContractProductMaterial;
import com.hzncc.flowable_diboot.interfacelog.annotation.InterfaceLog;
import com.sap.conn.jco.*;
import com.sap.conn.jco.ext.DestinationDataProvider;
import jakarta.annotation.PostConstruct;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileOutputStream;
import java.util.*;
import java.util.concurrent.Executors;

@Component
@Log4j2
@EnableScheduling
public class SapUtil {

    // SAP server
    @Value("${jco.host}")
    private String JCO_ASPHOST;
    // SAP system number
    @Value("${jco.sysnr}")
    private String JCO_SYSNR;
    // SAP client
    @Value("${jco.client}")
    private String JCO_CLIENT;
    // SAP user name
    @Value("${jco.user}")
    private String JCO_USER;
    // SAP user password
    @Value("${jco.pw}")
    private String JCO_PASSWD;
    // SAP language
    @Value("${jco.lang}")
    private String JCO_LANG;
    // MAX connection
    @Value("${jco.pool.capacity}")
    private String JCO_POOL_CAPACITY;
    // MAX thread
    @Value("${jco.pool.limit}")
    private String JCO_PEAK_LIMIT;
    // SAP ROUTER
    @Value("${jco.router}")
    private String JCO_SAPROUTER;

    private static final String ABAP_AS_POOLED = "ABAP_AS_WITH_POOL";

    @PostConstruct
    public void preheatConnectionPool() {
        Executors.newSingleThreadExecutor().submit(() -> {
            try {
                long start = System.currentTimeMillis();
                createDataFile(ABAP_AS_POOLED, "jcoDestination", buildConnectProperties());
                log.debug("Sap配置文件创建耗时{}", System.currentTimeMillis() - start);
                JCoDestination destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);
                destination.ping();
                log.info("SAP预载完成，耗时{}", System.currentTimeMillis() - start);
            } catch (JCoException e) {
                log.error("SAP连接失败", e);
            }
        });
    }

    @Scheduled(fixedRate = 5 * 60 * 1000) // 每5分钟执行
    public void keepAlive() {
        try {
            JCoDestination destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);
            destination.ping();
            log.debug("SAP连接心跳检测成功");
        } catch (JCoException e) {
            log.warn("SAP连接心跳检测异常，尝试重建连接", e);
            preheatConnectionPool(); // 触发重新连接
        }
    }


    private Properties buildConnectProperties() {
        Properties connectProperties = new Properties();
        // SAP server location
        connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, JCO_ASPHOST);
        // SAP system number
        connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, JCO_SYSNR);
        // SAP client
        connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, JCO_CLIENT);
        // SAP user ID
        connectProperties.setProperty(DestinationDataProvider.JCO_USER, JCO_USER);
        // SAP user PW
        connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, JCO_PASSWD);
        // SAP language
        connectProperties.setProperty(DestinationDataProvider.JCO_LANG, JCO_LANG);
        // MAX connection
        connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, JCO_POOL_CAPACITY);
        // MAX connection threads
        connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, JCO_PEAK_LIMIT);
        // 连接最大存活时间
        connectProperties.setProperty(DestinationDataProvider.JCO_EXPIRATION_TIME, "1440");
        // 连接检查间隔
        connectProperties.setProperty(DestinationDataProvider.JCO_EXPIRATION_PERIOD, "10");
        // 最大等待获取连接时间
        connectProperties.setProperty(DestinationDataProvider.JCO_MAX_GET_TIME, "5");

        return connectProperties;
    }

    /**
     * 链接sap
     *
     * @return
     * @author 陈广煜
     * @date 2024/5/28
     */
    public JCoDestination connect() {
        long start = System.currentTimeMillis();
        createDataFile(ABAP_AS_POOLED, "jcoDestination", buildConnectProperties());
        JCoDestination destination = null;
        try {
            destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);
//            destination.ping();
        } catch (JCoException e) {
            log.error("连接SAP失败: {}" , e.getMessage());
        }
        log.debug("SAP JCO获取连接耗时{}", System.currentTimeMillis() - start);
        return destination;
    }

    /**
     * Establish SAP interface
     *
     * @param name
     * @param suffix
     * @param properties
     * @author 陈广煜
     * @date 2024/5/28
     */
    private void createDataFile(String name, String suffix, Properties properties) {
        File cfg = new File(name + "." + suffix);
        if (cfg.exists()) {
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(cfg, false);
            properties.store(fos, "for tests only !");
            fos.close();
        } catch (Exception e) {
            System.out.println("Create Data file fault, error msg: " + e.toString());
            throw new RuntimeException("Unable to create the destination file " + cfg.getName(), e);
        }
    }

    /**
     * 执行sap函数
     *
     * @param funStr 函数名
     * @param param  参数
     * @return 返回值
     * @throws JCoException
     * @author 陈广煜
     * @date 2024/5/28
     */
    public JCoParameterList exe(String funStr, Map<String, Object> param, Map<String, Object> ext) throws JCoException {
        JCoDestination connect = connect();
        JCoFunction function = connect.getRepository().getFunction(funStr);
        JCoParameterList importParameterList = function.getImportParameterList();
        importParameterList.setValue("IS_DEL", "");
        JCoStructure struct = importParameterList.getStructure("IM_ZSSD001");
        for (Map.Entry<String, Object> en : param.entrySet()) {
            struct.setValue(en.getKey(), en.getValue());
        }
        if (Objects.equals(ext.get("type"), "create")) {
            struct.setValue("LANGU", "1");
            struct.setValue("AKONT", "1122000003");
            struct.setValue("ZUAWA", "001");
            struct.setValue("VSBED", "01");
            struct.setValue("WAERS", "CNY");
            struct.setValue("STREET", ext.get("contactAddress")); // 通讯地址
            String consignee = (String) ext.get("consignee");
            String phone = (String) ext.get("consigneePhone");
            struct.setValue("SMTP_ADDR", consignee + "," + phone);
        } else if (Objects.equals(ext.get("type"), "freeze")) {
            importParameterList.setValue("IS_DEL", "X");
        }
        function.execute(connect);
        JCoParameterList tableParameterList = function.getExportParameterList();
        return tableParameterList;
    }

    /**
     * 创建SAP合同
     *
     * @param funStr 函数名
     * @param param  参数
     * @param tab    表
     * @return
     * @throws JCoException
     * @author 陈广煜
     * @date 2024-09-06
     */
    @InterfaceLog(code = "SAP_CONTRACT_CREATE", name = "销售合同创建", direction = "CRM -> SAP")
    public Map<String, String> contractCreate(String funStr, Map<String, Object> param, List<Map<String, Object>> tab, String sapCode) throws JCoException {
        JCoDestination connect = connect();
        JCoFunction function = connect.getRepository().getFunction(funStr);
        JCoParameterList importParameterList = function.getImportParameterList();
        JCoStructure struct = importParameterList.getStructure("IS_IMPORT");
        for (Map.Entry<String, Object> en : param.entrySet()) {
            struct.setValue(en.getKey(), en.getValue());
        }
        if (S.equals(funStr, "ZRFC_CRM_VBAK_UPD")) {
            function.getImportParameterList().setValue("IF_VBELN", sapCode);
        }
        JCoTable table = function.getTableParameterList().getTable("IT_ITEM");
        for (int i = 0; i < tab.size(); i++) {
            table.appendRow();
            table.setRow(i);
            for (Map.Entry<String, Object> entry : tab.get(i).entrySet()) {
                table.setValue(entry.getKey(), entry.getValue());
            }
        }
        function.execute(connect);
        JCoStructure output = function.getExportParameterList().getStructure("ES_EXPORT");
        Map<String, String> res = new HashMap<>();
        String MSGTYPE = output.getString("MSGTYPE");//信息类型
        String EBELN = output.getString("EBELN"); // 合同号
        String MSGTXT = output.getString("MSGTXT"); // 信息文本
        res.put("code", MSGTYPE);
        res.put("contractCode", EBELN);
        res.put("msg", MSGTXT);
        return res;
    }

    /**
     * sap合同取消
     *
     * @param funStr 方法
     * @param tab    表
     * @param sapNo  sap合同号
     * @return
     * @author 陈广煜
     * @date 2024-09-07
     */
    @InterfaceLog(code = "SAP_CONTRACT_CANCEL", name = "销售合同取消", direction = "CRM -> SAP")
    public Map<String, String> contractCancel(String funStr, List<Map<String, Object>> tab, String sapNo) throws JCoException {
        JCoDestination connect = connect();
        JCoFunction function = connect.getRepository().getFunction(funStr);
        JCoParameterList importParameterList = function.getImportParameterList();
        importParameterList.setValue("IF_VBELN", sapNo);
        JCoTable itItem = function.getTableParameterList().getTable("IT_ITEM");
        for (int i = 0; i < tab.size(); i++) {
            itItem.appendRow();
            itItem.setRow(i);
            for (Map.Entry<String, Object> entry : tab.get(i).entrySet()) {
                itItem.setValue(entry.getKey(), entry.getValue());
            }
        }
        function.execute(connect);
        JCoStructure output = function.getExportParameterList().getStructure("ES_EXPORT");
        Map<String, String> res = new HashMap<>();
        String MSGTYPE = output.getString("MSGTYPE");//信息类型
        String EBELN = output.getString("EBELN"); // 合同号
        String MSGTXT = output.getString("MSGTXT"); // 信息文本
        res.put("code", MSGTYPE);
        res.put("contractCode", EBELN);
        res.put("msg", MSGTXT);
        return res;
    }

    @InterfaceLog(code = "SAP_PAYMENT_CERT", name = "合同收款凭证创建", direction = "CRM -> SAP")
    public Map<String, String> paymentCert(String funStr, Map<String, Object> param, List<Map<String, Object>> sapPaymentCertDetailVOList) throws JCoException {
        JCoDestination connect = connect();
        JCoFunction function = connect.getRepository().getFunction(funStr);
        JCoParameterList importParameterList = function.getImportParameterList();
        JCoStructure struct = importParameterList.getStructure("IT_HEAD");
        for (Map.Entry<String, Object> en : param.entrySet()) {
            struct.setValue(en.getKey(), en.getValue());
        }
        JCoTable itItem = function.getTableParameterList().getTable("IT_ITEM");
        for (int i = 0; i < sapPaymentCertDetailVOList.size(); i++) {
            itItem.appendRow();
            itItem.setRow(i);
            Map<String, Object> map = sapPaymentCertDetailVOList.get(i);
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                itItem.setValue(entry.getKey(), entry.getValue());
            }
        }
        function.execute(connect);
        JCoTable output = function.getTableParameterList().getTable("IT_MSG");
        Map<String, String> res = new HashMap<>();
        String MSGTYPE = output.getString("MSGID");//信息类型
        String EBELN = output.getString("BELNR"); // 合同号
        String MSGTXT = output.getString("MSGTXT"); // 信息文本
        res.put("code", MSGTYPE);
        res.put("sapCertCode", EBELN);
        res.put("msg", MSGTXT);
        return res;
    }

    @InterfaceLog(code = "SAP_REFUND_CERT", name = "合同退款凭证创建", direction = "CRM -> SAP")
    public Map<String, String> refundCert(String funStr, Map<String, Object> param, List<Map<String, Object>> sapPaymentCertDetailVOList) throws JCoException {
        JCoDestination connect = connect();
        JCoFunction function = connect.getRepository().getFunction(funStr);
        JCoParameterList importParameterList = function.getImportParameterList();
        JCoStructure struct = importParameterList.getStructure("IT_HEAD");
        for (Map.Entry<String, Object> en : param.entrySet()) {
            struct.setValue(en.getKey(), en.getValue());
        }
        JCoTable itItem = function.getTableParameterList().getTable("IT_ITEM");
        for (int i = 0; i < sapPaymentCertDetailVOList.size(); i++) {
            itItem.appendRow();
            itItem.setRow(i);
            Map<String, Object> map = sapPaymentCertDetailVOList.get(i);
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                itItem.setValue(entry.getKey(), entry.getValue());
            }
        }
        function.execute(connect);
        JCoTable output = function.getTableParameterList().getTable("IT_MSG");
        Map<String, String> res = new HashMap<>();
        String MSGTYPE = output.getString("MSGID");//信息类型
        String EBELN = output.getString("BELNR"); // 合同号
        String MSGTXT = output.getString("MSGTXT"); // 信息文本
        res.put("code", MSGTYPE);
        res.put("sapCertCode", EBELN);
        res.put("msg", MSGTXT);
        return res;
    }

    /**
     * 检查物料是否可以推送到SAP
     *
     * @param mrlList
     * @param salesOrg   销售组织
     * @param disChannel 分销渠道
     * @return 包含物料代码作为键及其状态作为值的映射
     * @throws JCoException 如果在执行SAP函数时发生错误
     */
    @InterfaceLog(code = "SAP_MRL_CHECK", name = "物料推送检查", direction = "CRM -> SAP")
    public Map<String, String> checkMrlCanPush(List<String> mrlList, String salesOrg, String disChannel) throws JCoException {
        // 获取sap连接
        JCoDestination connect = connect();
        // 获取函数
        JCoFunction function = connect.getRepository().getFunction("ZRFC_CRM_MATNR_STATU");
        // 获取导入参数
        JCoParameterList importParameterList = function.getImportParameterList();
        importParameterList.setValue("IF_VKORG", salesOrg);
        importParameterList.setValue("IF_VTWEG", disChannel);
        JCoTable itMatnr = function.getTableParameterList().getTable("IT_MATNR");
        for (int i = 0; i < mrlList.size(); i++) {
            itMatnr.appendRow();
            itMatnr.setRow(i);
            itMatnr.setValue("MATNR", mrlList.get(i));
        }
        // 执行函数
        function.execute(connect);
        Map<String, String> mrlStatus = new HashMap<>();
        do {
            String mrlCode = itMatnr.getString("MATNR");
            String status = itMatnr.getString("PSTAT_V");
            mrlStatus.put(mrlCode, status);
        } while (itMatnr.nextRow());
        return mrlStatus;
    }

    /**
     * 查询物料库存
     *
     * @param mrlList 物料列表
     * @param oaNum
     * @return
     * @throws JCoException
     */
    @InterfaceLog(code = "SAP_MRL_INVENTORY", name = "sap物料库存查询", direction = "CRM -> SAP")
    public Map<String, String> getSapInventory(List<ContractProductMaterial> mrlList, List<String> cells, String oaNum) throws JCoException {
        // 获取sap连接
        JCoDestination connect = connect();
        // 获取函数
        JCoFunction function = connect.getRepository().getFunction("ZRFC_CRM_MB52_AFTERCHG");
        JCoParameterList importParameterList = function.getImportParameterList();
        importParameterList.setValue("ZINTERFACE", "CRM_MB52");
        importParameterList.setValue("ZBUKRS", oaNum);
        // 获取导入参数
        JCoTable itMatnr = function.getTableParameterList().getTable("IT_MATNR");
        for (int i = 0; i < mrlList.size(); i++) {
            itMatnr.appendRow();
            itMatnr.setRow(i);
            itMatnr.setValue("MATNR", mrlList.get(i).getMaterialCode());
            itMatnr.setValue("MAKTX", mrlList.get(i).getMaterialDesc());
//            itMatnr.setValue("BDMNG",0);
        }
        // 库位
        JCoTable itLgort = function.getTableParameterList().getTable("IT_LGORT");
        for (int i = 0; i < cells.size(); i++) {
            itLgort.appendRow();
            itLgort.setRow(i);
            itLgort.setValue("LGORT", cells.get(i));
        }
        function.execute(connect);
        Map<String, String> mrlInventory = new HashMap<>();
        do {
            String mrlCode = itMatnr.getString("MATNR");
            String status = itMatnr.getString("BDMNG");
            mrlInventory.put(mrlCode, status);
        } while (itMatnr.nextRow());
        return mrlInventory;
    }

}
